Ten dokument opisuje sposoby, możliwości i techniki tworzenia modeli klasyfikacji w uczeniu maszynowym. Znajdziesz tu informacje o różnych algorytmach klasyfikacji, ich zastosowaniu w praktycznym przykładzie. Przedstawione są również przykłady kodów w języku Python, które pokazują jak zaimplementować modele klasyfikacji.
Mam nadzieję, że będzie to dla Ciebie przydatne! Miłej lektury!
Stworzenie modelu, który przewiduje, czy klient banku otrzyma kredyt hipoteczny.
II. Wczytanie i wstępne przetwarzanie danych
1. Wczytanie danych źródłówych
Dane do budowy modelu uczenia maszynowego zostały wygenerowne syntetycznie za pomocą kodu python.
Są to dane fikcyjne wygenerowane w celu demonstracyjnym.
Wygenerowane dane zawierają wiele zmiennych , które zostana wykorzystane do budowy modelu klasyfikacyjnego, które celem jest ustalenie czy klient banku w oparciu o wybrane zmienne/cechy otrzyma kredyt hipoteczny , czy nie otrzyma.
Wygenerowane dane zostały zapisane do pliku excel.
Code
# ====================================================================# 1. Wczytanie i wstępne przetwarzanie danych# ====================================================================## 1.1. Wczytanie danych z pliku Exceldf = pd.read_excel('synthetic_data.xlsx')
2. Podstawowe informacje o danych
Code
display(Markdown("**Wgląd w strukturę danych:**"))def analiza_dataframe(df):# Liczba kolumn liczba_kolumn =len(df.columns)# Liczba wierszy liczba_wierszy =len(df)# Liczba kolumn numerycznych, tekstowych, dat i boolowskich liczba_kolumn_numerycznych =len(df.select_dtypes(include='number').columns) liczba_kolumn_textowych =len(df.select_dtypes(include='object').columns) liczba_kolumn_dat =len(df.select_dtypes(include='datetime').columns) liczba_kolumn_bool =len(df.select_dtypes(include='bool').columns)# Tworzenie DataFrame z wynikami wyniki = pd.DataFrame({'Liczba kolumn': [liczba_kolumn],'Liczba wierszy': [liczba_wierszy],'Liczba kolumn numerycznych': [liczba_kolumn_numerycznych],'Liczba kolumn tekstowych': [liczba_kolumn_textowych],'Liczba kolumn dat': [liczba_kolumn_dat],'Liczba kolumn bool': [liczba_kolumn_bool] }) wyniki = wyniki.sort_values(by='Liczba kolumn numerycznych', ascending=False).reset_index(drop=True)return wynikiimport pandas as pddef informacje_o_dataframe(df, dodatkowe_kolumny_numeryczne=None): informacje = []if dodatkowe_kolumny_numeryczne isNone: dodatkowe_kolumny_numeryczne = []for kolumna in df.columns: typ_kolumny = df[kolumna].dtype unikalne_wartosci = df[kolumna].nunique() puste_wartosci = df[kolumna].isnull().sum() niepuste_wartosci = df[kolumna].count()if typ_kolumny =='object'or kolumna in dodatkowe_kolumny_numeryczne: unikalne_wartosci_lista = df[kolumna].unique()else: unikalne_wartosci_lista =" " informacje.append([kolumna, typ_kolumny, unikalne_wartosci, puste_wartosci, niepuste_wartosci, unikalne_wartosci_lista]) informacje_df = pd.DataFrame(informacje, columns=['Nazwa kolumny', 'Typ kolumny', 'Liczba unikalnych wartości', 'Liczba wartości pustych', 'Liczba wartości niepustych', 'Wartości unikatowe dla zmiennych kategorialnych']) informacje_df = informacje_df.sort_values(by='Typ kolumny').reset_index(drop=True)return informacje_df# Aby wyświetlić unikalne wartości dla kolumn tekstowych oraz 'ilość_dzieci', 'ilość_kredytów' i 'kredyt':informacje_df = informacje_o_dataframe(df, dodatkowe_kolumny_numeryczne=['ilość_dzieci', 'ilość_kredytów', 'kredyt'])display(analiza_dataframe(df))print()display(Markdown("""**Wygenerowane dane, są zbiorem, który będzie wykorzystany do modelowania predykcyjnego, w kontekście oceny ryzyka kredytowego. Poniżej znajduje się szczegółowy opis poszczególnych kolumn:**"""))display(informacje_df)display(Markdown("**Przykładowe dane - 8 losowych wierszy:**"))display(df.sample(8).T)
Wgląd w strukturę danych:
Liczba kolumn
Liczba wierszy
Liczba kolumn numerycznych
Liczba kolumn tekstowych
Liczba kolumn dat
Liczba kolumn bool
0
25
1003
16
9
0
0
Wygenerowane dane, są zbiorem, który będzie wykorzystany do modelowania predykcyjnego, w kontekście oceny ryzyka kredytowego. Poniżej znajduje się szczegółowy opis poszczególnych kolumn:
Nazwa kolumny
Typ kolumny
Liczba unikalnych wartości
Liczba wartości pustych
Liczba wartości niepustych
Wartości unikatowe dla zmiennych kategorialnych
0
wiek
int64
52
0
1003
1
dochody nie ewidencjonowane
int64
915
0
1003
2
ilość_dzieci
int64
5
0
1003
[0, 1, 2, 3, 4]
3
ilość_kredytów
int64
3
0
1003
[0, 1, 2]
4
kredyt
int64
2
0
1003
[1, 0]
5
oszczędności
float64
979
1
1002
6
dochód
float64
983
0
1003
7
dochody z zagranicy
float64
955
1
1002
8
zadłużenie
float64
974
0
1003
9
koszty rachunku
float64
62
1
1002
10
liczba wizyt w oddziale
float64
57
1
1002
11
dodatkowy_przychód
float64
999
1
1002
12
inwestycje
float64
999
1
1002
13
kwota_kredytu
float64
999
1
1002
14
wartość_nieruchomości
float64
995
1
1002
15
koszty_utrzymania
float64
998
1
1002
16
czy_karta_kredytowa
object
2
1
1002
[tak, nie, nan]
17
czy_korzysta_z_aplikacji
object
2
0
1003
[Nie, Tak]
18
wykształcenie
object
4
2
1001
[średnie, zawodowe, podstawowe, wyższe, nan]
19
sytuacja_mieszkaniowa
object
3
0
1003
[własne, wynajem, mieszkanie z rodzicami]
20
forma_wynagrodzenia
object
3
0
1003
[miesięczne, tygodniowe, za wykonanie]
21
źródło_dochodu
object
10
2
1001
[Dochody z działalności gospodarczej, Wynagrodzenie za dzieło, Renta, Wynagrodzenie etatowe, Stypendium, Wynagrodzenie za zlecenie, Emerytura, Dochody kapitałowe, Dochody z najmu, Inne źródło dochodu, nan]
22
typ_zabezpieczenia
object
3
0
1003
[poręczenie, hipoteka, gwarancja bankowa]
23
czy_aktywny_online
object
2
0
1003
[Nie, Tak]
24
czy_ubezpieczenie
object
2
2
1001
[nie, tak, nan]
Przykładowe dane - 8 losowych wierszy:
884
663
766
909
737
932
515
992
wiek
46
34
39
61
49
25
47
37
dochód
4947.19
3784.36
9341.89
5285.89
2864.39
4069.38
4756.5
7377.83
oszczędności
0.0
10516.52
19663.87
32571.49
23423.38
11107.59
23278.8
20720.37
zadłużenie
9679.61
11561.89
5123.17
6316.15
9203.19
14437.52
12377.15
122.56
ilość_kredytów
1
0
0
0
1
1
0
0
ilość_dzieci
0
0
1
0
2
1
0
0
wartość_nieruchomości
318029.49
50000.0
513558.67
210943.12
333191.84
324748.05
229628.96
394588.82
kwota_kredytu
30467.11
40135.07
46076.47
61321.53
64430.28
37284.95
40505.1
115950.2
inwestycje
49420.08
60619.46
21074.95
87597.57
42697.12
2778.99
7016.16
3712.48
dodatkowy_przychód
20016.29
17739.88
20044.39
50365.46
24663.34
4650.83
5585.64
3295.01
koszty_utrzymania
22552.37
16216.45
5643.36
1814.07
1439.24
22694.72
59119.58
7222.85
czy_karta_kredytowa
tak
nie
tak
tak
tak
nie
tak
tak
czy_ubezpieczenie
tak
tak
tak
nie
tak
nie
nie
tak
koszty rachunku
99.98
100.02
99.88
99.97
99.99
99.98
99.89
99.92
liczba wizyt w oddziale
50.07
50.14
50.23
49.79
50.08
49.84
50.14
49.85
dochody z zagranicy
1119.0
11619.0
710.0
5083.0
24009.0
1248.0
5782.0
11340.0
dochody nie ewidencjonowane
1475
2200
2852
411
4226
9769
4780
11241
wykształcenie
średnie
zawodowe
średnie
podstawowe
podstawowe
podstawowe
zawodowe
zawodowe
sytuacja_mieszkaniowa
mieszkanie z rodzicami
mieszkanie z rodzicami
własne
mieszkanie z rodzicami
wynajem
własne
wynajem
mieszkanie z rodzicami
forma_wynagrodzenia
za wykonanie
miesięczne
za wykonanie
za wykonanie
miesięczne
tygodniowe
za wykonanie
miesięczne
źródło_dochodu
Wynagrodzenie etatowe
Emerytura
Renta
Emerytura
Dochody z działalności gospodarczej
Dochody z działalności gospodarczej
Emerytura
Emerytura
typ_zabezpieczenia
gwarancja bankowa
poręczenie
hipoteka
gwarancja bankowa
gwarancja bankowa
hipoteka
gwarancja bankowa
poręczenie
czy_aktywny_online
Tak
Nie
Tak
Nie
Nie
Nie
Nie
Nie
czy_korzysta_z_aplikacji
Nie
Nie
Nie
Nie
Nie
Nie
Tak
Tak
kredyt
0
0
1
1
1
0
0
0
Code
# Wydzielenie X (zmienne objaśniające) i y (zmienna docelowa)X = df.drop('kredyt', axis=1)y = df['kredyt']print()# Identyfikacja zmiennych liczbowychnumeric_variables = X.select_dtypes(include=['int64', 'float64'])numeric_variable_names = numeric_variables.columns.tolist()# Identyfikacja zmiennych tekstowychnon_numeric_variables = X.select_dtypes(exclude=['int64', 'float64'])non_numeric_variable_names = non_numeric_variables.columns.tolist()# Tworzenie DataFrame z nazwami zmiennych liczbowych i tekstowychvariables_type_info = {'Zmienne liczbowe': numeric_variable_names,'Zmienne tekstowe': non_numeric_variable_names}variables_type_df = pd.DataFrame(dict([(k, pd.Series(v)) for k, v in variables_type_info.items()]))variables_type_df = variables_type_df.fillna('') print()display(Markdown('**Podział zmiennych/cech wg ich rodzaju:**'))display(Markdown('**Nazwy zmiennych liczbowych i tekstowych:**'))display(variables_type_df)statistics = df.describe().transpose()display(Markdown('**Statystyki dla wszystkich cech zmiennych numerycznych:**'))display(statistics)# Wyliczanie statystyk dla zmiennych tekstowychtext_variable_stats = non_numeric_variables.describe().transpose()display(Markdown('**Statystyki dla zmiennych tekstowych:**'))display(text_variable_stats)print()
Podział zmiennych/cech wg ich rodzaju:
Nazwy zmiennych liczbowych i tekstowych:
Zmienne liczbowe
Zmienne tekstowe
0
wiek
czy_karta_kredytowa
1
dochód
czy_ubezpieczenie
2
oszczędności
wykształcenie
3
zadłużenie
sytuacja_mieszkaniowa
4
ilość_kredytów
forma_wynagrodzenia
5
ilość_dzieci
źródło_dochodu
6
wartość_nieruchomości
typ_zabezpieczenia
7
kwota_kredytu
czy_aktywny_online
8
inwestycje
czy_korzysta_z_aplikacji
9
dodatkowy_przychód
10
koszty_utrzymania
11
koszty rachunku
12
liczba wizyt w oddziale
13
dochody z zagranicy
14
dochody nie ewidencjonowane
Statystyki dla wszystkich cech zmiennych numerycznych:
count
mean
std
min
25%
50%
75%
max
wiek
1003.0
39.689930
9.701975
18.00
33.0000
40.000
46.0000
78.00
dochód
1003.0
5161.768574
1948.846383
1000.00
3788.6700
5128.950
6460.2700
11386.22
oszczędności
1002.0
20134.858363
9647.506070
0.00
13510.8475
20003.675
26573.1500
59262.38
zadłużenie
1003.0
9944.368126
5057.192748
0.00
6309.6450
10003.410
13342.3700
26215.46
ilość_kredytów
1003.0
0.508475
0.531144
0.00
0.0000
0.000
1.0000
2.00
ilość_dzieci
1003.0
0.916251
0.823171
0.00
0.0000
1.000
1.0000
4.00
wartość_nieruchomości
1002.0
297336.029601
102086.284994
50000.00
224959.8800
294525.365
369809.4800
652905.52
kwota_kredytu
1002.0
53338.115259
46372.642332
5002.64
20467.4225
39781.045
71533.3675
334186.55
inwestycje
1002.0
32034.764192
28930.993099
2007.23
10713.3575
23054.615
44472.8500
230340.08
dodatkowy_przychód
1002.0
10671.875309
10251.354979
501.58
3446.6250
7345.340
14825.0575
80761.78
koszty_utrzymania
1002.0
16066.356238
15335.171439
1018.07
5173.1275
11010.245
22132.2900
100663.36
koszty rachunku
1002.0
99.999960
0.100700
99.66
99.9400
100.000
100.0600
100.34
liczba wizyt w oddziale
1002.0
50.000768
0.097684
49.72
49.9300
50.000
50.0700
50.33
dochody z zagranicy
1002.0
5098.725549
4878.981433
1.00
1617.5000
3761.500
6943.7500
35348.00
dochody nie ewidencjonowane
1003.0
3011.296112
2921.332462
3.00
933.0000
2121.000
4067.0000
20556.00
kredyt
1003.0
0.480558
0.499871
0.00
0.0000
0.000
1.0000
1.00
Statystyki dla zmiennych tekstowych:
count
unique
top
freq
czy_karta_kredytowa
1002
2
tak
532
czy_ubezpieczenie
1001
2
tak
512
wykształcenie
1001
4
zawodowe
266
sytuacja_mieszkaniowa
1003
3
wynajem
346
forma_wynagrodzenia
1003
3
za wykonanie
355
źródło_dochodu
1001
10
Dochody z działalności gospodarczej
180
typ_zabezpieczenia
1003
3
hipoteka
344
czy_aktywny_online
1003
2
Tak
505
czy_korzysta_z_aplikacji
1003
2
Nie
517
2.1. Ocena rozkładów zmiennych
Code
# Tworzenie wykresów Altair dla zmiennych liczbowychaltair_charts = []for column in numeric_variables.columns: chart = alt.Chart(numeric_variables.reset_index()).transform_density( column, as_=[column, 'density'] ).mark_area(opacity=0.5, color='skyblue').encode( x=alt.X(column, title=column), y=alt.Y('density:Q', title='Density') ).properties( title=f"KDE dla {column}", width=200, height=150 ) altair_charts.append(chart)altair_display = alt.vconcat(*[alt.hconcat(*altair_charts[i:i+5]).resolve_scale(x='independent', y='independent') for i inrange(0, len(altair_charts), 5)])display(Markdown('**Wykres KDE zmiennych numerycznych do oceny skośności rozkładu:**'))altair_display.display()print()# Wybór zmiennych z rozkładem skośnymskewed_variables = numeric_variables.apply(lambda x: x.skew()).sort_values(ascending=False)skewed_variables = skewed_variables[abs(skewed_variables) >0.5] # Przyjęcie progu skośności > 0.5# Wyświetlenie zmiennych z rozkładem skośnym jako DataFrameskewed_variables_df = skewed_variables.reset_index()skewed_variables_df.columns = ['Variable', 'Skewness']display(Markdown('**Zmienna z rozkładem skośnym jako DataFrame:**'))display(skewed_variables_df)display(Markdown('Zmienne te zostaną przekształcone do rozkładu normalnego w procesie preprosesingu:'))display(Markdown('Rozkłd normalny jest wymagany w więkzoci algorytmów modeli klasyfikacyjnych:'))# Tworzenie wykresów barowych dla zmiennych tekstowychbar_charts = []for column in non_numeric_variables.columns: chart = alt.Chart(non_numeric_variables).mark_bar().encode( x=alt.X(f"{column}:N", title=column, sort='-y'), y=alt.Y('count()', title='Liczność'), color=alt.Color(f"{column}:N", legend=None) ).properties( title=f"Rozkład wartości dla {column}", width=200, height=180 ) bar_charts.append(chart)bar_display = alt.vconcat(*[alt.hconcat(*bar_charts[i:i+5]).resolve_scale(x='independent', y='independent') for i inrange(0, len(bar_charts), 5)])print()print()display(Markdown('**Wykresy zmiennych kategorialnych do oceny rozkładu:**'))bar_display.display()
Wykres KDE zmiennych numerycznych do oceny skośności rozkładu:
Zmienna z rozkładem skośnym jako DataFrame:
Variable
Skewness
0
dodatkowy_przychód
2.156402
1
dochody z zagranicy
1.913067
2
kwota_kredytu
1.899588
3
koszty_utrzymania
1.872375
4
dochody nie ewidencjonowane
1.854802
5
inwestycje
1.799129
Zmienne te zostaną przekształcone do rozkładu normalnego w procesie preprosesingu:
Rozkłd normalny jest wymagany w więkzoci algorytmów modeli klasyfikacyjnych:
Wykresy zmiennych kategorialnych do oceny rozkładu:
3. Przygotowanie i czyszczenie danych
3.1. Analiza duplikatów
Code
display(Markdown(f"Sprawdzenie czy w danych występują duplikaty"))display(Markdown(f"Identyfikacja:"))display(Markdown(f"- Czy w tabeli danych znajdują się zduplikowane-powielone * KOLUMNY * (z tymi samymi wartosciami)? :"))zduplikowane_kolumny = df.columns[df.columns.duplicated()].tolist()if zduplikowane_kolumny: display(Markdown(f"* Zduplikowane kolumny: {zduplikowane_kolumny}"))else: display(Markdown(f" -Brak zduplikowanych kolumn"))display(Markdown(f"- Czy w tabeli danych znajdują się zduplikowane-powielone * WIERSZE * (z tymi samymi wartosciami)? :"))if df.duplicated().any(): display(Markdown(f" -Zduplikowane wiersze: {df[df.duplicated()].shape[0]}")) display(Markdown(f"Rozmiar tabeli z duplikatami: {df.shape}"))#display(df[df.duplicated()])else: display(Markdown(f" -Brak zduplikowanych wierszy"))display(Markdown(f"* Rozwiązaniem jest dokonanie zidentyfikowanie dublujących się danych , a następnie ich usunięcie "))display(Markdown(f" UWAGA: Usunięto wiersze z duplikującymi się danymi"))df.drop_duplicates(inplace=True)display(Markdown(f"Rozmiar tabeli po usunięciu duplikatów: {df.shape}"))print()
Sprawdzenie czy w danych występują duplikaty
Identyfikacja:
Czy w tabeli danych znajdują się zduplikowane-powielone * KOLUMNY * (z tymi samymi wartosciami)? :
-Brak zduplikowanych kolumn
Czy w tabeli danych znajdują się zduplikowane-powielone * WIERSZE * (z tymi samymi wartosciami)? :
-Zduplikowane wiersze: 3
Rozmiar tabeli z duplikatami: (1003, 25)
Rozwiązaniem jest dokonanie zidentyfikowanie dublujących się danych , a następnie ich usunięcie
UWAGA: Usunięto wiersze z duplikującymi się danymi
Rozmiar tabeli po usunięciu duplikatów: (1000, 25)
3.2. Analiza brakujących danych
Code
import pandas as pdimport altair as altfrom IPython.display import display, Markdowndef braki_sprawdzenie(dane): liczba = dane.isnull().sum().sum() proc = (liczba / (dane.shape[0] * dane.shape[1]) *100).round(2) display(Markdown('**Analiza brakujących danych:**')) display(Markdown('='*45))if liczba ==0: display(Markdown('W tabeli nie stwierdzono brakujących danych!'))else: display(Markdown(f'Liczba brakujących danych w tabeli: {liczba}')) display(Markdown(f'Procent brakujących danych w tabeli: {proc}%')) rows_with_missing_data = dane[dane.isnull().any(axis=1)] brakujace_dane = rows_with_missing_data.isnull().sum(axis=0) udzial_brakujacych_danych = ((rows_with_missing_data.isnull().sum(axis=0) / dane.shape[0]) *100).round(1) wyniki = pd.DataFrame({'liczba': brakujace_dane, 'proc': udzial_brakujacych_danych}) display(Markdown('**Brakujące dane w zmiennych (kolumny):**')) display(wyniki) brakujace_dane = rows_with_missing_data.isnull().sum(axis=1) udzial_brakujacych_danych = (rows_with_missing_data.isnull().sum(axis=1) / dane.shape[1] *100).round(1) wyniki = pd.DataFrame({'liczba': brakujace_dane, 'proc': udzial_brakujacych_danych}) display(Markdown('**Brakujące dane w obserwacjach (wiersze):**')) display(wyniki)#display(Markdown('**Tabela z brakującymi danymi:**'))#display(rows_with_missing_data)braki_sprawdzenie(df)
Analiza brakujących danych:
=============================================
Liczba brakujących danych w tabeli: 16
Procent brakujących danych w tabeli: 0.06%
Brakujące dane w zmiennych (kolumny):
liczba
proc
wiek
0
0.0
dochód
0
0.0
oszczędności
1
0.1
zadłużenie
0
0.0
ilość_kredytów
0
0.0
ilość_dzieci
0
0.0
wartość_nieruchomości
1
0.1
kwota_kredytu
1
0.1
inwestycje
1
0.1
dodatkowy_przychód
1
0.1
koszty_utrzymania
1
0.1
czy_karta_kredytowa
1
0.1
czy_ubezpieczenie
2
0.2
koszty rachunku
1
0.1
liczba wizyt w oddziale
1
0.1
dochody z zagranicy
1
0.1
dochody nie ewidencjonowane
0
0.0
wykształcenie
2
0.2
sytuacja_mieszkaniowa
0
0.0
forma_wynagrodzenia
0
0.0
źródło_dochodu
2
0.2
typ_zabezpieczenia
0
0.0
czy_aktywny_online
0
0.0
czy_korzysta_z_aplikacji
0
0.0
kredyt
0
0.0
Brakujące dane w obserwacjach (wiersze):
liczba
proc
182
1
4.0
203
1
4.0
226
1
4.0
234
1
4.0
710
1
4.0
713
1
4.0
718
1
4.0
728
1
4.0
789
1
4.0
805
1
4.0
817
1
4.0
852
1
4.0
891
2
8.0
922
1
4.0
923
1
4.0
3.3 Identyfikacja i usunięcie zmiennych o niskiej wariancji tj. zmiennych quasi-stałych
Code
# Wydzielenie X (zmienne objaśniające) i y (zmienna docelowa)X = df.drop('kredyt', axis=1)y = df['kredyt']# ==================================================================================# Identyfikacja i usunięcie zmiennych o niskiej wariancji tj. zmiennych quasi-stałych# ================================================================================display(Markdown('**Analiza zmiennych o niskiej wariancji:**'))display(Markdown('- przyjęty próg variancji 0.01, zmienne pozniżej tego progu zostaną usunięte:'))variance_threshold =0.01# Próg wariancji# Obliczenie wariancji dla zmiennych liczbowychvariances = X.select_dtypes(include=['number']).var()formatted_variances = variances.apply(lambda x: f"{x:.4f}")formatted_variances = formatted_variances.sort_values(ascending=False).to_frame()formatted_variances.index.name ='Nazwa kolumny'formatted_variances.columns = ['Wariancja']display(formatted_variances)# Wykrywanie zmiennych o niskiej wariancjilow_variance = variances[variances < variance_threshold].index.tolist()display(Markdown(f'**Zmienne o niskiej wariancji: {low_variance}**'))# Usunięcie zmiennych o niskiej wariancjiprint()if low_variance: X = X.drop(columns=low_variance) display(Markdown(f'**Usunięto {len(low_variance)} zmiennych o niskiej wariancji**'))else: display(Markdown('**"Brak zmiennych o niskiej wariancji**'))
Analiza zmiennych o niskiej wariancji:
przyjęty próg variancji 0.01, zmienne pozniżej tego progu zostaną usunięte:
Wariancja
Nazwa kolumny
wiek
94.1283
oszczędności
93074373.3666
dochody nie ewidencjonowane
8534183.3524
inwestycje
837002361.7127
dochód
3798002.2241
zadłużenie
25575198.4917
dochody z zagranicy
23804459.8217
koszty_utrzymania
235167483.0662
kwota_kredytu
2150421956.8182
dodatkowy_przychód
105090278.9076
wartość_nieruchomości
10421609583.9712
ilość_dzieci
0.6776
ilość_kredytów
0.2821
koszty rachunku
0.0101
liczba wizyt w oddziale
0.0095
Zmienne o niskiej wariancji: [‘liczba wizyt w oddziale’]
Usunięto 1 zmiennych o niskiej wariancji
3.4 Identyfikacja i obsługa wartości odstających
Code
# ====================================================================# Identyfikacja i obsługa wartości odstających# ====================================================================import pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport seaborn as snsimport altair as altfrom scipy.stats import zscorefrom scipy.stats.mstats import winsorizefrom IPython.display import display, Markdowndisplay(Markdown('**Analiza wartości odstających:**'))display(Markdown('- wartości odstające wykryte metodą (z_scores) > 3) zostaną przekształcone metodą winsoryzacji:'))numeric_cols = X.select_dtypes(include=['number']).columns# Tworzenie wykresów pudełkowych dla wszystkich zmiennych liczbowychnum_cols =len(numeric_cols)fig, axes = plt.subplots(nrows=(num_cols //7) +1, ncols=min(7, num_cols), figsize=(15, 2* ((num_cols //7) +1)))axes = axes.flatten() if num_cols >1else [axes]for i, column inenumerate(numeric_cols): sns.boxplot(y=X[column], ax=axes[i], flierprops={'marker': 'o', 'color': 'red', 'markersize': 3}) axes[i].set_title(f"Boxplot dla {column}") axes[i].set_xlabel("")for j inrange(i +1, len(axes)): fig.delaxes(axes[j])plt.tight_layout()for ax in axes: ax.tick_params(axis='both', which='major', labelsize=6) ax.title.set_fontsize(6) axes[i].set_ylabel("")for ax in axes: ax.tick_params(axis='both', which='major', labelsize=6) ax.title.set_fontsize(6) ax.set_ylabel("") # Remove y-axis label#ax.yaxis.set_visible(False) # Hide y-axis ticks and labels ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False)#ax.spines['left'].set_visible(False)#ax.spines['bottom'].set_visible(False)plt.show()# Obliczenie Z-score dla zmiennych liczbowychnumeric_cols = X.select_dtypes(include=['number']).columnsz_scores = X[numeric_cols].apply(zscore)# Wykrywanie obserwacji odstających (|Z-score| > 3)outliers_mask = (np.abs(z_scores) >3).any(axis=1)display(Markdown(f'**Znaleziono {outliers_mask.sum()} obserwacji odstających:**'))# Tworzenie tabeli przed transformacjąoutliers_before = X.loc[outliers_mask, numeric_cols]display(Markdown("### Wartości odstające przed transformacją"))display(outliers_before)# Usuwanie lub transformacja wartości odstającychdecision ="transform"# Można zmienić na "remove"if decision =="remove": X = X[~outliers_mask] y = y[~outliers_mask]print("Usunięto obserwacje odstające")elif decision =="transform":for col in numeric_cols: X[col] = winsorize(X[col], limits=[0.05, 0.05]) display(Markdown(f'**Przekształcono wartości odstające metodą winsoryzacji:**'))# Tworzenie tabeli po transformacji outliers_after = X.loc[outliers_mask, numeric_cols] display(Markdown("### Wartości odstające po transformacji")) display(outliers_after)# Tworzenie wykresów pudełkowych dla wszystkich zmiennych liczbowychnum_cols =len(numeric_cols)fig, axes = plt.subplots(nrows=(num_cols //7) +1, ncols=min(7, num_cols), figsize=(15, 2* ((num_cols //7) +1)))axes = axes.flatten() if num_cols >1else [axes]for i, column inenumerate(numeric_cols): sns.boxplot(y=X[column], ax=axes[i], flierprops={'marker': 'o', 'color': 'red', 'markersize': 3}) axes[i].set_title(f"Boxplot dla {column}") axes[i].set_xlabel("")for j inrange(i +1, len(axes)): fig.delaxes(axes[j])plt.tight_layout()for ax in axes: ax.tick_params(axis='both', which='major', labelsize=6) ax.title.set_fontsize(6) axes[i].set_ylabel("")for ax in axes: ax.tick_params(axis='both', which='major', labelsize=6) ax.title.set_fontsize(6) ax.set_ylabel("") # Remove y-axis label#ax.yaxis.set_visible(False) # Hide y-axis ticks and labels ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False)#ax.spines['left'].set_visible(False)#ax.spines['bottom'].set_visible(False)plt.show()# Aktualizacja danych po zmianachX = X.reset_index(drop=True)y = y.reset_index(drop=True)
Analiza wartości odstających:
wartości odstające wykryte metodą (z_scores) > 3) zostaną przekształcone metodą winsoryzacji:
Znaleziono 26 obserwacji odstających:
Wartości odstające przed transformacją
wiek
dochód
oszczędności
zadłużenie
ilość_kredytów
ilość_dzieci
wartość_nieruchomości
kwota_kredytu
inwestycje
dodatkowy_przychód
koszty_utrzymania
koszty rachunku
dochody z zagranicy
dochody nie ewidencjonowane
0
44
7798.71
13248.22
460.96
0
0
188591.86
5487.39
41236.33
9972.89
7163.91
100.18
9376.0
12134
18
30
4359.31
26354.18
7744.21
0
1
285265.98
107967.37
5059.39
2762.26
4406.41
99.99
6536.0
12704
93
36
5103.89
8611.67
12098.05
1
2
212880.70
147894.01
12418.28
939.66
3889.66
99.97
3949.0
15100
121
30
7374.06
27846.04
6939.17
0
2
314775.59
8857.38
2425.39
26280.46
15684.08
100.02
1009.0
13796
173
43
4874.81
21517.58
13880.14
0
1
167950.66
40162.71
43848.54
4575.83
57742.29
100.09
11681.0
13733
199
28
7728.28
24878.72
12186.57
1
1
264175.56
65560.67
58321.88
4539.31
29364.17
100.03
1970.0
15806
209
78
8499.15
19992.91
12256.13
0
1
234025.14
67968.03
14363.64
4996.97
24935.95
99.96
125.0
7368
213
46
6789.85
15479.10
17616.19
0
1
169541.06
88361.44
23517.18
8099.94
6287.69
99.90
5154.0
13933
296
48
6838.15
24183.98
18381.97
0
1
207086.37
122160.82
31860.09
9855.34
14494.13
100.11
9240.0
12977
394
51
4269.36
19165.62
19862.71
1
0
209719.11
30884.95
30956.01
6304.47
2273.41
99.95
5017.0
13937
401
34
6073.31
21768.21
12848.80
1
0
218598.93
121264.87
116871.13
612.62
34277.72
99.97
86.0
14542
405
46
3486.41
33404.61
0.00
1
1
433696.91
10184.46
58051.37
2908.65
8241.26
100.10
25507.0
17018
478
70
3225.02
6613.94
15767.95
0
0
275597.16
7568.06
22988.74
16505.83
7930.62
99.94
326.0
1510
561
60
3505.58
20856.87
6216.18
1
0
434609.98
19802.85
20438.91
1561.31
3432.02
99.84
7972.0
14053
613
28
4844.33
19934.79
11979.10
0
1
519664.61
51952.27
23577.50
2248.22
48041.79
100.11
167.0
18510
615
41
11386.22
26683.40
13673.19
0
0
299828.58
13736.07
11585.62
4583.04
30921.77
99.91
4125.0
4173
677
57
8042.63
4351.97
9248.79
1
2
210274.70
10401.23
58011.15
6363.50
26022.50
100.10
1962.0
15865
716
41
5663.96
26255.08
26215.46
2
1
404839.99
65150.84
25072.01
23888.27
5720.76
99.97
5504.0
12331
752
30
3691.85
15341.94
16264.21
0
2
273545.41
81253.88
68617.40
4845.30
3716.00
100.14
2710.0
13875
796
39
4361.89
25731.28
9178.32
1
4
453914.23
175886.47
40774.52
3779.41
14430.01
100.00
49.0
311
867
43
7253.41
11133.19
18268.09
0
0
219400.83
12943.72
17720.37
1685.59
15535.85
100.01
4688.0
20556
925
24
3161.23
22522.40
8318.07
0
0
167844.42
26770.82
18850.17
4204.16
11782.64
100.03
8441.0
13540
952
27
3188.54
24666.71
20920.48
1
0
153502.53
99184.43
28559.40
23830.55
44013.55
99.95
340.0
12827
957
44
11275.50
17634.45
8802.49
1
1
480608.91
185400.73
16098.41
22509.74
8934.26
99.92
3363.0
5520
963
47
6290.43
9434.42
13844.20
1
0
265785.13
46110.36
6331.05
4912.94
15693.17
100.03
1307.0
12375
982
30
3746.57
27324.92
25760.28
1
2
272311.06
7996.41
19920.14
16751.82
15642.90
99.92
4579.0
3
Przekształcono wartości odstające metodą winsoryzacji:
Wartości odstające po transformacji
wiek
dochód
oszczędności
zadłużenie
ilość_kredytów
ilość_dzieci
wartość_nieruchomości
kwota_kredytu
inwestycje
dodatkowy_przychód
koszty_utrzymania
koszty rachunku
dochody z zagranicy
dochody nie ewidencjonowane
0
44
7798.71
13248.22
1068.12
0
0
188591.86
7823.49
41236.33
9972.89
7163.91
100.16
9376.0
8933
18
30
4359.31
26354.18
7744.21
0
1
285265.98
107967.37
5059.39
2762.26
4406.41
99.99
6536.0
8933
93
36
5103.89
8611.67
12098.05
1
2
212880.70
147894.01
12418.28
939.66
3889.66
99.97
3949.0
8933
121
30
7374.06
27846.04
6939.17
0
2
314775.59
8857.38
3471.47
26280.46
15684.08
100.02
1009.0
8933
173
43
4874.81
21517.58
13880.14
0
1
167950.66
40162.71
43848.54
4575.83
48800.00
100.09
11681.0
8933
199
28
7728.28
24878.72
12186.57
1
1
264175.56
65560.67
58321.88
4539.31
29364.17
100.03
1970.0
8933
209
56
8390.10
19992.91
12256.13
0
1
234025.14
67968.03
14363.64
4996.97
24935.95
99.96
252.0
7368
213
46
6789.85
15479.10
17616.19
0
1
169541.06
88361.44
23517.18
8099.94
6287.69
99.90
5154.0
8933
296
48
6838.15
24183.98
18345.35
0
1
207086.37
122160.82
31860.09
9855.34
14494.13
100.11
9240.0
8933
394
51
4269.36
19165.62
18345.35
1
0
209719.11
30884.95
30956.01
6304.47
2273.41
99.95
5017.0
8933
401
34
6073.31
21768.21
12848.80
1
0
218598.93
121264.87
89959.02
937.25
34277.72
99.97
252.0
8933
405
46
3486.41
33404.61
1068.12
1
1
433696.91
10184.46
58051.37
2908.65
8241.26
100.10
15480.0
8933
478
56
3225.02
6613.94
15767.95
0
0
275597.16
7823.49
22988.74
16505.83
7930.62
99.94
326.0
1510
561
56
3505.58
20856.87
6216.18
1
0
434609.98
19802.85
20438.91
1561.31
3432.02
99.84
7972.0
8933
613
28
4844.33
19934.79
11979.10
0
1
468149.25
51952.27
23577.50
2248.22
48041.79
100.11
252.0
8933
615
41
8390.10
26683.40
13673.19
0
0
299828.58
13736.07
11585.62
4583.04
30921.77
99.91
4125.0
4173
677
56
8042.63
4351.97
9248.79
1
2
210274.70
10401.23
58011.15
6363.50
26022.50
100.10
1962.0
8933
716
41
5663.96
26255.08
18345.35
1
1
404839.99
65150.84
25072.01
23888.27
5720.76
99.97
5504.0
8933
752
30
3691.85
15341.94
16264.21
0
2
273545.41
81253.88
68617.40
4845.30
3716.00
100.14
2710.0
8933
796
39
4361.89
25731.28
9178.32
1
2
453914.23
148158.69
40774.52
3779.41
14430.01
100.00
252.0
311
867
43
7253.41
11133.19
18268.09
0
0
219400.83
12943.72
17720.37
1685.59
15535.85
100.01
4688.0
8933
925
24
3161.23
22522.40
8318.07
0
0
167844.42
26770.82
18850.17
4204.16
11782.64
100.03
8441.0
8933
952
27
3188.54
24666.71
18345.35
1
0
153502.53
99184.43
28559.40
23830.55
44013.55
99.95
340.0
8933
957
44
8390.10
17634.45
8802.49
1
1
468149.25
148158.69
16098.41
22509.74
8934.26
99.92
3363.0
5520
963
47
6290.43
9434.42
13844.20
1
0
265785.13
46110.36
6331.05
4912.94
15693.17
100.03
1307.0
8933
982
30
3746.57
27324.92
18345.35
1
2
272311.06
7996.41
19920.14
16751.82
15642.90
99.92
4579.0
171
3.5 Identyfikacja zależności zmiennych numerycznych
Code
# ====================================================================# Identyfikacja korelacji zmiennych numerycznych# ====================================================================display(Markdown('**Identyfikacja korelacji zmiennych numerycznych:**'))# Wybór tylko zmiennych numerycznychnumeric_df = df.select_dtypes(include=['int64', 'float64'])# Obliczenie macierzy korelacjicorr_matrix = numeric_df.corr()# Wyświetlenie macierzy korelacjiprint("Macierz korelacji:")display(corr_matrix)# Wizualizacja macierzy korelacjiplt.figure(figsize=(8, 6))sns.heatmap(corr_matrix, annot=True, fmt=".2f", cmap="coolwarm", cbar=True)plt.title("Macierz korelacji - Heatmap")plt.show()# ====================================================================# Identyfikacja skorelowanych zmiennych powyżej progu# ====================================================================threshold =0.8# Próg korelacjihigh_corr_pairs = []# Przeglądanie macierzy korelacjifor i inrange(len(corr_matrix.columns)):for j inrange(i +1, len(corr_matrix.columns)): # Unikamy duplikatów (dolna trójkątna macierz)ifabs(corr_matrix.iloc[i, j]) > threshold: high_corr_pairs.append((corr_matrix.columns[i], corr_matrix.columns[j], corr_matrix.iloc[i, j]))# Wyświetlenie listy skorelowanych zmiennychif high_corr_pairs: display(Markdown("### Zmienne o korelacji powyżej progu 0.8:"))for var1, var2, corr in high_corr_pairs:print(f"- {var1} i {var2}: korelacja {corr:.2f}")else:print("Brak zmiennych o korelacji powyżej progu 0.8.")# ====================================================================# 4. Aktualizacja analiz po czyszczeniu danych# ====================================================================display(Markdown('**Podsumowanie po czyszczeniu danych :**'))print(f"Ostateczny kształt danych: {X.shape[0]} wierszy, {X.shape[1]} kolumn")
Identyfikacja korelacji zmiennych numerycznych:
Macierz korelacji:
wiek
dochód
oszczędności
zadłużenie
ilość_kredytów
ilość_dzieci
wartość_nieruchomości
kwota_kredytu
inwestycje
dodatkowy_przychód
koszty_utrzymania
koszty rachunku
liczba wizyt w oddziale
dochody z zagranicy
dochody nie ewidencjonowane
kredyt
wiek
1.000000
-0.046639
0.024964
-0.014288
-0.033285
-0.004130
-0.030030
-0.006264
-0.031703
0.055229
-0.011509
0.006703
0.051518
0.036673
0.034434
-0.006492
dochód
-0.046639
1.000000
-0.010178
-0.057793
-0.036383
0.024518
-0.004762
0.022782
0.008036
-0.003681
0.036732
-0.036670
0.054777
-0.022277
-0.012434
0.054433
oszczędności
0.024964
-0.010178
1.000000
0.028001
0.030572
-0.022237
-0.027681
0.000805
0.008326
-0.027316
-0.080645
-0.073243
-0.051948
0.029832
-0.006996
0.151617
zadłużenie
-0.014288
-0.057793
0.028001
1.000000
0.046840
0.035293
0.011309
0.014149
-0.040385
-0.027177
0.045472
-0.023470
0.009197
0.005628
0.040392
-0.219454
ilość_kredytów
-0.033285
-0.036383
0.030572
0.046840
1.000000
-0.050875
0.005212
0.008814
0.002883
0.017065
-0.036128
-0.017176
-0.067408
0.013275
-0.057005
-0.071738
ilość_dzieci
-0.004130
0.024518
-0.022237
0.035293
-0.050875
1.000000
0.019430
0.013273
-0.000255
0.024309
0.055334
0.012849
0.049479
0.015345
0.020415
0.013017
wartość_nieruchomości
-0.030030
-0.004762
-0.027681
0.011309
0.005212
0.019430
1.000000
0.035759
-0.033775
0.009160
0.020943
-0.029952
-0.013657
0.001931
-0.053165
0.006162
kwota_kredytu
-0.006264
0.022782
0.000805
0.014149
0.008814
0.013273
0.035759
1.000000
-0.060638
-0.040581
0.018862
-0.007033
-0.007249
-0.005624
-0.049049
-0.499861
inwestycje
-0.031703
0.008036
0.008326
-0.040385
0.002883
-0.000255
-0.033775
-0.060638
1.000000
-0.025145
0.008791
-0.034319
0.040481
-0.038507
-0.014695
0.181930
dodatkowy_przychód
0.055229
-0.003681
-0.027316
-0.027177
0.017065
0.024309
0.009160
-0.040581
-0.025145
1.000000
0.001901
-0.010524
0.030210
0.042244
0.008020
0.193804
koszty_utrzymania
-0.011509
0.036732
-0.080645
0.045472
-0.036128
0.055334
0.020943
0.018862
0.008791
0.001901
1.000000
-0.063305
-0.042506
-0.054683
-0.008443
-0.332900
koszty rachunku
0.006703
-0.036670
-0.073243
-0.023470
-0.017176
0.012849
-0.029952
-0.007033
-0.034319
-0.010524
-0.063305
1.000000
0.020257
-0.021308
0.013123
0.006931
liczba wizyt w oddziale
0.051518
0.054777
-0.051948
0.009197
-0.067408
0.049479
-0.013657
-0.007249
0.040481
0.030210
-0.042506
0.020257
1.000000
-0.003488
0.000883
0.059525
dochody z zagranicy
0.036673
-0.022277
0.029832
0.005628
0.013275
0.015345
0.001931
-0.005624
-0.038507
0.042244
-0.054683
-0.021308
-0.003488
1.000000
0.034620
0.073105
dochody nie ewidencjonowane
0.034434
-0.012434
-0.006996
0.040392
-0.057005
0.020415
-0.053165
-0.049049
-0.014695
0.008020
-0.008443
0.013123
0.000883
0.034620
1.000000
0.028158
kredyt
-0.006492
0.054433
0.151617
-0.219454
-0.071738
0.013017
0.006162
-0.499861
0.181930
0.193804
-0.332900
0.006931
0.059525
0.073105
0.028158
1.000000
Brak zmiennych o korelacji powyżej progu 0.8.
Podsumowanie po czyszczeniu danych :
Ostateczny kształt danych: 1003 wierszy, 23 kolumn
3.6 Identyfikacja zależności zmiennych nominalnych
Code
import pandas as pdimport numpy as npfrom scipy.stats import chi2_contingencyfrom sklearn.preprocessing import OrdinalEncoderimport matplotlib.pyplot as pltimport seaborn as snsdef cramers_v(confusion_matrix): chi2 = chi2_contingency(confusion_matrix)[0] n = confusion_matrix.sum() min_dim =min(confusion_matrix.shape) -1# Zapobiega dzieleniu przez zeroif n ==0or min_dim ==0:return0.0else:return np.sqrt((chi2 / n) / min_dim)def analiza_korelacji_kredyt_kategoria(df, zmienne_kategorialne, kredyt_zmienna='kredyt'): wyniki = []for zmienna in zmienne_kategorialne:# 1. Obliczenie tabeli kontyngencji tab_kont = pd.crosstab(df[zmienna], df[kredyt_zmienna])# 2. Wykonanie testu chi-kwadrat chi2, p, dof, oczekiwane = chi2_contingency(tab_kont)# 3. Obliczenie V Cramera v_cramer = cramers_v(tab_kont.values) # Dodano obliczenie V Cramera# 4. Obliczenie średniego kredytu dla każdej kategorii sredni_kredyt = df.groupby(zmienna)[kredyt_zmienna].mean() licznosc_kategorii = df[zmienna].value_counts()# Dodanie wyników do listy wyniki.append({'Zmienna kategorialna': zmienna,'Liczba kategorii': len(tab_kont),'Chi2': chi2,'P-wartość': p,'V Cramera': v_cramer, # Dodano V Cramera do wyników#'Średni kredyt dla kategorii': sredni_kredyt.to_dict(), # Usunięto kolumnę z wynikami'Liczność kategorii': licznosc_kategorii.to_dict() })# Konwersja wyników na DataFrame wyniki_df = pd.DataFrame(wyniki)return wyniki_dfdef wizualizacja_korelacji_kredyt_kategoria(df, wyniki_df, kredyt_zmienna='kredyt'):# Przygotowanie danych do mapy cieplnej cramer_matrix = pd.DataFrame(index=wyniki_df['Zmienna kategorialna'], columns=['V Cramera'])for index, row in wyniki_df.iterrows(): cramer_matrix.loc[row['Zmienna kategorialna'], 'V Cramera'] = row['V Cramera']# Konwersja kolumny 'V Cramera' na typ numeryczny cramer_matrix['V Cramera'] = pd.to_numeric(cramer_matrix['V Cramera'], errors='coerce')# Mapa cieplna plt.figure(figsize=(8, 4)) sns.heatmap(cramer_matrix, annot=True, cmap='viridis', fmt=".2f") plt.title("Mapa Cieplna miary V Cramera dla Zmiennych Kategorialnych i Zmiennej 'kredyt'") plt.xlabel("Zmienna 'kredyt'") plt.ylabel("Zmienne Kategorialne") plt.show()zmienne_kategorialne = ['czy_karta_kredytowa', 'czy_korzysta_z_aplikacji', 'wykształcenie', 'sytuacja_mieszkaniowa', 'forma_wynagrodzenia', 'źródło_dochodu', 'typ_zabezpieczenia', 'czy_aktywny_online', 'czy_ubezpieczenie']wyniki_analizy = analiza_korelacji_kredyt_kategoria(df, zmienne_kategorialne)display(wyniki_analizy)wizualizacja_korelacji_kredyt_kategoria(df, wyniki_analizy)
1.Wydzielenie ze zbioru zmienych objaśniających [X] oraz zmiennej celu [Y]
Podział zbioru danych na:
Zmienną docelową (y) – zawierającą wartości kolumny ‘kredyt’, którą chcemy przewidywać.
Zmiennie objaśniające (X) – czyli wszystkie pozostałe kolumny oprócz ‘kredyt’, które będą używane jako cechy wejściowe do modelu predykcyjnego.
Code
# Wydzielenie X (zmienne objaśniające) i y (zmienna docelowa)X = df.drop('kredyt', axis=1)y = df['kredyt']# Tworzenie DataFrame z nazwami zmiennych predykcyjnych i zmiennej celuvariables_info = {'Zmienne predykcyjne': X.columns.tolist(),'Zmienna celu': [y.name]}variables_df = pd.DataFrame(dict([(k, pd.Series(v)) for k, v in variables_info.items()]))variables_df = variables_df.fillna('') # Zamiana NaN na pusty wierszdisplay(Markdown('**Nazwy zmiennych predykcyjnych i zmiennej celu:**'))display(variables_df)
Nazwy zmiennych predykcyjnych i zmiennej celu:
Zmienne predykcyjne
Zmienna celu
0
wiek
kredyt
1
dochód
2
oszczędności
3
zadłużenie
4
ilość_kredytów
5
ilość_dzieci
6
wartość_nieruchomości
7
kwota_kredytu
8
inwestycje
9
dodatkowy_przychód
10
koszty_utrzymania
11
czy_karta_kredytowa
12
czy_ubezpieczenie
13
koszty rachunku
14
liczba wizyt w oddziale
15
dochody z zagranicy
16
dochody nie ewidencjonowane
17
wykształcenie
18
sytuacja_mieszkaniowa
19
forma_wynagrodzenia
20
źródło_dochodu
21
typ_zabezpieczenia
22
czy_aktywny_online
23
czy_korzysta_z_aplikacji
2.Podział danych na zbiory treningowy, testowy
Podział danych na zbiór treningowy i testowy jest kluczowy w uczeniu maszynowym, ponieważ pozwala ocenić rzeczywistą skuteczność modelu na nowych, niewidzianych wcześniej danych. - Uniknięcie przeuczenia (overfittingu) – trenując model tylko na części danych, możemy ocenić jego jakość na niezależnym zbiorze testowym. - Poprawna ocena modelu – zbiór testowy symuluje nowe, nieznane dane i pozwala sprawdzić, jak model generalizuje. - Zachowanie proporcji klas (stratify=y) – zapewnia realistyczne warunki dla algorytmu uczenia maszynowego, szczególnie ważne przy niezrównoważonych klasach.
Code
# ====================================================================# 2. Podział danych na zbiory treningowy, testowy# ====================================================================# Podział danych na zbiory treningowy i testowyX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y, shuffle=True)# Tworzenie słownika z rozmiarami zbiorów danychshape_dict = {"Dane": ["X - zmienna objaśniacjąca (cały zbiór)", "y - zmienna celu (cały zbiór)", "X_train", "y_train", "X_test", "y_test"],"Rozmiar": [X.shape, y.shape, X_train.shape, y_train.shape, X_test.shape, y_test.shape],}print()# Tworzenie DataFrame z rozmiarami zbiorów danychshape_df = pd.DataFrame(shape_dict)# Wyświetlenie DataFrame z rozmiarami zbiorów danychprint("Rozmiary zbiorów danych:")display(shape_df)# Tworzenie słownika z proporcjami zmiennej docelowej w zbiorach danychprop_y = {'Struktura danych "y_train"': y_train.value_counts(normalize=True) *100,'Struktura danych "y_test"': y_test.value_counts(normalize=True) *100,}print()# Tworzenie DataFrame z proporcjami zmiennej docelowejproporcje_y = pd.DataFrame(prop_y)# Wyświetlenie DataFrame z proporcjami zmiennej docelowej, zaokrąglonymi do dwóch miejsc po przecinkuprint("Proporcje zmiennej docelowej w zbiorach danych:")display(proporcje_y.round(2))print()# Obliczanie struktury danychdata_structure_info = {'Zbiór danych': ['X_train', 'X_test'],'Procent danych': [ (X_train.shape[0] / X.shape[0]) *100, (X_test.shape[0] / X.shape[0]) *100, ],}print()import altair as altimport pandas as pd# Tworzenie DataFramecounts = y_train.value_counts().reset_index()counts.columns = ['Klasa', 'Liczność']counts['Procent'] = counts['Liczność'] / counts['Liczność'].sum() *100# Tworzenie wykresuchart = ( alt.Chart(counts) .mark_bar() .encode( x=alt.X('Klasa:N', title='Klasa'), y=alt.Y('Liczność:Q', title='Liczność'), color=alt.Color('Klasa:N', scale=alt.Scale(scheme='set2')), tooltip=['Klasa', 'Liczność', 'Procent'] ))# Dodanie wartości procentowychtext = ( alt.Chart(counts) .mark_text(dy=-10) .encode( x=alt.X('Klasa:N'), y=alt.Y('Liczność:Q'), text=alt.Text('Procent:Q', format='.1f') ))# Wyświetlenie wykresu(chart + text).properties(title='Rozkład klas w zbiorze treningowym', width=500, height=200).interactive()
Rozmiary zbiorów danych:
Dane
Rozmiar
0
X - zmienna objaśniacjąca (cały zbiór)
(1003, 24)
1
y - zmienna celu (cały zbiór)
(1003,)
2
X_train
(802, 24)
3
y_train
(802,)
4
X_test
(201, 24)
5
y_test
(201,)
Proporcje zmiennej docelowej w zbiorach danych:
Struktura danych "y_train"
Struktura danych "y_test"
kredyt
0
52.0
51.74
1
48.0
48.26
3.Preprocesing danych
3.1. Utworzenie struktury przepływ przekształceń
Przekształcanie danych poprzez imputację braków, kodowanie cech kategorycznych, skalowanie, binaryzację, grupowanie wieku, transformację zmiennych skośnych oraz tworzenie transformerów, zapewni kompleksowe przygotowanie danych do modelowania.
Code
# ====================================================================# 3. Przetwarzanie danych# ====================================================================import pandas as pd# Definiujemy danedata = [ ('czy_karta_kredytowa', 'binarna (kategoryczna)', 'Kodowanie wartości na 0 (Nie) i 1 (Tak)', 'Binarizer', 'SimpleImputer (most_frequent)'), ('czy_ubezpieczenie', 'binarna (kategoryczna)', 'Kodowanie wartości na 0 (Nie) i 1 (Tak)', 'Binarizer', 'SimpleImputer (most_frequent)'), ('czy_aktywny_online', 'binarna (kategoryczna)', 'Kodowanie wartości na 0 (Nie) i 1 (Tak)', 'Binarizer', 'SimpleImputer (most_frequent)'), ('czy_korzysta_z_aplikacji', 'binarna (kategoryczna)', 'Kodowanie wartości na 0 (Nie) i 1 (Tak)', 'Binarizer', 'SimpleImputer (most_frequent)'), ('wykształcenie', 'porządkowa (kategoryczna)', 'Przekształcenie kategorii na wartości liczbowe według hierarchii: podstawowe < zawodowe < średnie < wyższe', 'OrdinalEncoder', 'SimpleImputer (most_frequent)'), ('typ_zabezpieczenia', 'nominalna (kategoryczna)', 'Zamiana kategorii na zestaw zmiennych 0/1 (one-hot encoding)', 'OneHotEncoder', 'SimpleImputer (most_frequent)'), ('sytuacja_mieszkaniowa', 'nominalna (kategoryczna)', 'Zamiana kategorii na zestaw zmiennych 0/1 (one-hot encoding)', 'OneHotEncoder', 'SimpleImputer (most_frequent)'), ('forma_wynagrodzenia', 'nominalna (kategoryczna)', 'Zamiana kategorii na zestaw zmiennych 0/1 (one-hot encoding)', 'OneHotEncoder', 'SimpleImputer (most_frequent)'), ('ilość_kredytów', 'numeryczna', 'Standaryzacja wartości do rozkładu N(0,1)', 'StandardScaler', 'SimpleImputer (mean)'), ('liczba wizyt w oddziale', 'numeryczna', 'Standaryzacja wartości do rozkładu N(0,1)', 'StandardScaler', 'SimpleImputer (mean)'), ('koszty rachunku', 'numeryczna', 'Standaryzacja wartości do rozkładu N(0,1)', 'StandardScaler', 'SimpleImputer (mean)'), ('zadłużenie', 'numeryczna', 'Standaryzacja wartości do rozkładu N(0,1)', 'StandardScaler', 'SimpleImputer (mean)'), ('oszczędności', 'numeryczna', 'Standaryzacja wartości do rozkładu N(0,1)', 'StandardScaler', 'SimpleImputer (mean)'), ('wartość_nieruchomości', 'numeryczna', 'Standaryzacja wartości do rozkładu N(0,1)', 'StandardScaler', 'SimpleImputer (mean)'), ('dochody nie ewidencjonowane', 'binarna (kategoryczna)', 'Binarizacja wartości', 'Binarizer', 'SimpleImputer (most_frequent)'), ('źródło_dochodu', 'nominalna (kategoryczna)', 'Transformacja rzadkich wartości', 'Custom Transformer', 'SimpleImputer (most_frequent)'), ('wiek', 'numeryczna (kategoryczna)', 'Podział na przedziały wiekowe (14-22, 23-39, 40-64, >65)', 'pd.cut()', 'SimpleImputer (most_frequent)'), ('dochód', 'numeryczna', 'Obliczenie dochodu na osobę (dochód/liczba osób)', 'Custom Transformer: DochodNaOsobe', 'SimpleImputer (mean)'), ('ilość_dzieci', 'numeryczna', 'Wykorzystanie w obliczeniach dochodu na osobę', 'Custom Transformer: DochodNaOsobe', 'SimpleImputer (mean)'), ('kwota_kredytu', 'numeryczna', 'Obliczenie stosunku kwoty kredytu do wartości nieruchomości', 'Custom Transformer: StosunekKredytuDoNieruchomosci', 'SimpleImputer (mean)'), ('koszty_utrzymania', 'numeryczna', 'Standaryzacja wartości do rozkładu N(0,1)', 'StandardScaler', 'SimpleImputer (mean)'), ('inwestycje', 'numeryczna', 'Standaryzacja wartości do rozkładu N(0,1)', 'StandardScaler', 'SimpleImputer (mean)'), ('dodatkowy_przychód', 'numeryczna', 'Standaryzacja wartości do rozkładu N(0,1)', 'StandardScaler', 'SimpleImputer (mean)')]# Tworzenie DataFrame połączoną kolumną 'typ zmiennej' i bez 'Pipeline'df = pd.DataFrame(data, columns=['nazwa zmiennej', 'typ zmiennej', 'rodzaj przekształcenia', 'funkcja w Pythonie użyta do przekształcenia', 'imputacja braków'])# Ustawienie opcji wyświetlania, aby zawijać długie tekstypd.set_option('display.max_colwidth', None)print("Preprocesing danych - Zestawienie dokonanych przekształceń na danych:")# Wyświetlenie tabelidisplay(df)
Preprocesing danych - Zestawienie dokonanych przekształceń na danych:
nazwa zmiennej
typ zmiennej
rodzaj przekształcenia
funkcja w Pythonie użyta do przekształcenia
imputacja braków
0
czy_karta_kredytowa
binarna (kategoryczna)
Kodowanie wartości na 0 (Nie) i 1 (Tak)
Binarizer
SimpleImputer (most_frequent)
1
czy_ubezpieczenie
binarna (kategoryczna)
Kodowanie wartości na 0 (Nie) i 1 (Tak)
Binarizer
SimpleImputer (most_frequent)
2
czy_aktywny_online
binarna (kategoryczna)
Kodowanie wartości na 0 (Nie) i 1 (Tak)
Binarizer
SimpleImputer (most_frequent)
3
czy_korzysta_z_aplikacji
binarna (kategoryczna)
Kodowanie wartości na 0 (Nie) i 1 (Tak)
Binarizer
SimpleImputer (most_frequent)
4
wykształcenie
porządkowa (kategoryczna)
Przekształcenie kategorii na wartości liczbowe według hierarchii: podstawowe < zawodowe < średnie < wyższe
OrdinalEncoder
SimpleImputer (most_frequent)
5
typ_zabezpieczenia
nominalna (kategoryczna)
Zamiana kategorii na zestaw zmiennych 0/1 (one-hot encoding)
OneHotEncoder
SimpleImputer (most_frequent)
6
sytuacja_mieszkaniowa
nominalna (kategoryczna)
Zamiana kategorii na zestaw zmiennych 0/1 (one-hot encoding)
OneHotEncoder
SimpleImputer (most_frequent)
7
forma_wynagrodzenia
nominalna (kategoryczna)
Zamiana kategorii na zestaw zmiennych 0/1 (one-hot encoding)
OneHotEncoder
SimpleImputer (most_frequent)
8
ilość_kredytów
numeryczna
Standaryzacja wartości do rozkładu N(0,1)
StandardScaler
SimpleImputer (mean)
9
liczba wizyt w oddziale
numeryczna
Standaryzacja wartości do rozkładu N(0,1)
StandardScaler
SimpleImputer (mean)
10
koszty rachunku
numeryczna
Standaryzacja wartości do rozkładu N(0,1)
StandardScaler
SimpleImputer (mean)
11
zadłużenie
numeryczna
Standaryzacja wartości do rozkładu N(0,1)
StandardScaler
SimpleImputer (mean)
12
oszczędności
numeryczna
Standaryzacja wartości do rozkładu N(0,1)
StandardScaler
SimpleImputer (mean)
13
wartość_nieruchomości
numeryczna
Standaryzacja wartości do rozkładu N(0,1)
StandardScaler
SimpleImputer (mean)
14
dochody nie ewidencjonowane
binarna (kategoryczna)
Binarizacja wartości
Binarizer
SimpleImputer (most_frequent)
15
źródło_dochodu
nominalna (kategoryczna)
Transformacja rzadkich wartości
Custom Transformer
SimpleImputer (most_frequent)
16
wiek
numeryczna (kategoryczna)
Podział na przedziały wiekowe (14-22, 23-39, 40-64, >65)
pd.cut()
SimpleImputer (most_frequent)
17
dochód
numeryczna
Obliczenie dochodu na osobę (dochód/liczba osób)
Custom Transformer: DochodNaOsobe
SimpleImputer (mean)
18
ilość_dzieci
numeryczna
Wykorzystanie w obliczeniach dochodu na osobę
Custom Transformer: DochodNaOsobe
SimpleImputer (mean)
19
kwota_kredytu
numeryczna
Obliczenie stosunku kwoty kredytu do wartości nieruchomości
Cel: Przygotowanie danych do analizy poprzez transformację różnych typów zmiennych do formatu numerycznego, co jest wymagane przez większość algorytmów uczenia maszynowego
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
['dodatkowy_przychód', 'dochody z zagranicy', 'kwota_kredytu', 'koszty_utrzymania', 'inwestycje']
SimpleImputer()
PowerTransformer()
['liczba wizyt w oddziale']
passthrough
4.Selekcja danych
4.1. Ważnosć cech - wg modelu Random Forest
Code
# ====================================================================# SELEKCJA CECH (WAŻNOŚĆ CECH RANDOM FOREST)# ====================================================================# Inicjalizacja klasyfikatora Random Forestrf = RandomForestClassifier(n_estimators=100, random_state=42)# Utworzenie obiektu SelectFromModelfeature_selection_rf = SelectFromModel(rf, threshold=0.005) # Możesz dostosować próg# Selekcja cech na przetransformowanych danychX_train_selected_rf = feature_selection_rf.fit_transform(X_train_transformed, y_train)X_test_selected_rf = feature_selection_rf.transform(X_test_transformed)# Pobranie ważności cech z modelu Random Forestfeature_importances = rf.fit(X_train_transformed, y_train).feature_importances_# Tworzenie DataFrame z cechami i ich ważnościąfeature_importance_df = pd.DataFrame({'Feature': X_train_transformed.columns,'Importance': feature_importances}).sort_values(by='Importance', ascending=False)# Wyświetlenie posortowanego DataFramedisplay(feature_importance_df)features=20# Domyślna wartość, zmieniana w siatce hiperparametrów# ))# ])# Wizualizacja ważności cechplt.figure(figsize=(10, 6))sns.barplot(x=feature_importance_df['Importance'], y=feature_importance_df['Feature'], palette='viridis')plt.title('Ważność cech wg Random Forest', fontsize=14)plt.xlabel('Ważność cechy', fontsize=9)plt.ylabel('Cechy', fontsize=9)plt.tight_layout()plt.show()
Feature
Importance
30
skewed__kwota_kredytu
0.284308
31
skewed__koszty_utrzymania
0.129690
16
numeric__zadłużenie
0.069330
28
skewed__dodatkowy_przychód
0.062352
17
numeric__oszczędności
0.052736
32
skewed__inwestycje
0.048836
29
skewed__dochody z zagranicy
0.043522
33
remainder__liczba wizyt w oddziale
0.039338
18
numeric__wartość_nieruchomości
0.039319
27
dochod_na_osobe__dochód_na_osobę
0.039256
15
numeric__koszty rachunku
0.032868
4
ordinal__wykształcenie
0.013296
26
wiek_grupy__wiek
0.010398
1
binary__czy_ubezpieczenie
0.010384
14
numeric__ilość_kredytów
0.009254
5
onehot__typ_zabezpieczenia_gwarancja bankowa
0.007555
2
binary__czy_aktywny_online
0.007508
19
binarize__dochody nie ewidencjonowane
0.007415
13
onehot__forma_wynagrodzenia_za wykonanie
0.007222
3
binary__czy_korzysta_z_aplikacji
0.006910
22
oherare__źródło_dochodu_Renta
0.006857
10
onehot__sytuacja_mieszkaniowa_własne
0.006604
21
oherare__źródło_dochodu_Emerytura
0.006517
9
onehot__sytuacja_mieszkaniowa_wynajem
0.006375
7
onehot__typ_zabezpieczenia_poręczenie
0.005986
6
onehot__typ_zabezpieczenia_hipoteka
0.005949
8
onehot__sytuacja_mieszkaniowa_mieszkanie z rodzicami
0.005724
11
onehot__forma_wynagrodzenia_miesięczne
0.005412
24
oherare__źródło_dochodu_Wynagrodzenie za zlecenie
0.005209
0
binary__czy_karta_kredytowa
0.005202
12
onehot__forma_wynagrodzenia_tygodniowe
0.005073
20
oherare__źródło_dochodu_Dochody z działalności gospodarczej
0.005002
25
oherare__źródło_dochodu_infrequent_sklearn
0.004649
23
oherare__źródło_dochodu_Wynagrodzenie etatowe
0.003942
4.2. Ważnosć cech - wg mutual information
Code
from sklearn.feature_selection import mutual_info_classif, SelectKBestimport pandas as pd# Obliczenie wartości Mutual Information dla każdej cechymi_scores = mutual_info_classif(X_train_transformed, y_train)# Tworzenie DataFrame z cechami i ich ważnościąmi_importance_df = pd.DataFrame({'Feature': X_train_transformed.columns,'Importance': mi_scores}).sort_values(by='Importance', ascending=False)# Wyświetlenie posortowanego DataFramedisplay(mi_importance_df)# Wizualizacja ważności cech za pomocą wykresu słupkowego#import matplotlib.pyplot as plt# Wizualizacja ważności cech za pomocą wykresu słupkowegoplt.figure(figsize=(10, 6))plt.barh(mi_importance_df['Feature'], mi_importance_df['Importance'])plt.xlabel('Ważność')plt.ylabel('Cechy')plt.title('Ważność cech na podstawie Mutual Information')plt.gca().invert_yaxis() # Inwersja osi Y, aby najważniejsze cechy były na górzeplt.show()
Feature
Importance
30
skewed__kwota_kredytu
0.218761
31
skewed__koszty_utrzymania
0.086960
6
onehot__typ_zabezpieczenia_hipoteka
0.035686
5
onehot__typ_zabezpieczenia_gwarancja bankowa
0.030142
32
skewed__inwestycje
0.023819
24
oherare__źródło_dochodu_Wynagrodzenie za zlecenie
0.021098
22
oherare__źródło_dochodu_Renta
0.020379
23
oherare__źródło_dochodu_Wynagrodzenie etatowe
0.019366
14
numeric__ilość_kredytów
0.018595
8
onehot__sytuacja_mieszkaniowa_mieszkanie z rodzicami
0.018120
26
wiek_grupy__wiek
0.017923
18
numeric__wartość_nieruchomości
0.014118
15
numeric__koszty rachunku
0.011451
13
onehot__forma_wynagrodzenia_za wykonanie
0.009517
10
onehot__sytuacja_mieszkaniowa_własne
0.008505
29
skewed__dochody z zagranicy
0.008192
7
onehot__typ_zabezpieczenia_poręczenie
0.007596
27
dochod_na_osobe__dochód_na_osobę
0.003928
33
remainder__liczba wizyt w oddziale
0.003305
16
numeric__zadłużenie
0.002991
1
binary__czy_ubezpieczenie
0.001209
28
skewed__dodatkowy_przychód
0.000970
0
binary__czy_karta_kredytowa
0.000203
4
ordinal__wykształcenie
0.000000
2
binary__czy_aktywny_online
0.000000
3
binary__czy_korzysta_z_aplikacji
0.000000
17
numeric__oszczędności
0.000000
12
onehot__forma_wynagrodzenia_tygodniowe
0.000000
11
onehot__forma_wynagrodzenia_miesięczne
0.000000
9
onehot__sytuacja_mieszkaniowa_wynajem
0.000000
25
oherare__źródło_dochodu_infrequent_sklearn
0.000000
19
binarize__dochody nie ewidencjonowane
0.000000
20
oherare__źródło_dochodu_Dochody z działalności gospodarczej
0.000000
21
oherare__źródło_dochodu_Emerytura
0.000000
5.2. Tworzenie selektora
Utworzenie selektora na bazie SelectKBest(score_func=mutual_info_classif, k=30)) ])
Code
# Pipeline do selekcji zmiennych metodą mutual informationfeature_selection_pipeline_mutual = Pipeline([('selector', SelectKBest(score_func=mutual_info_classif, k=30)) ])# # Pipeline selekcji cech na podstawie Feature Importance z RFfeature_selection_pipeline_rf = Pipeline([ ('feature_importance', SelectFromModel( RandomForestClassifier(n_estimators=100, random_state=42), threshold=-np.inf, # Umożliwia selekcję na podstawie max_features lub threshold='mean' max_features=25# Domyślna wartość, zmieniana w siatce hiperparametrów ))])feature_selection_pipeline_xgb = Pipeline([ ('xgb', SelectFromModel( XGBClassifier(n_estimators=100, random_state=42), threshold='mean', # Możemy ustawić próg na 'mean' lub jakąś konkretną wartość max_features=25# Maksymalna liczba cech ))])from sklearn.tree import DecisionTreeClassifierfrom sklearn.feature_selection import SelectFromModelfeature_selection_pipeline_dt = Pipeline([ ('dt', SelectFromModel( DecisionTreeClassifier(random_state=42), threshold='mean', # Możemy użyć 'mean' lub ustawić odpowiedni próg max_features=25# Liczba cech do wyboru ))])from sklearn.feature_selection import SelectKBestfrom sklearn.feature_selection import f_classiffeature_selection_pipeline_anova = Pipeline([ ('anova', SelectKBest(f_classif, k=25)) # Wybiera 25 najlepszych cech na podstawie ANOVA F-test])from sklearn.feature_selection import SelectKBestfrom sklearn.feature_selection import chi2feature_selection_pipeline_chi2 = Pipeline([ ('chi2', SelectKBest(chi2, k=25)) # Wybiera 25 najlepszych cech na podstawie testu chi-kwadrat])# Każdy z powyższych pipeline'ów implementuje inną metodę selekcji cech:# Lasso (L1 regularization) - wykorzystuje regularizację L1, która skutkuje "wygaszaniem" mniej ważnych cech.# Chi-squared - stosowane głównie dla zmiennych kategorycznych w celu oceny niezależności cech od klasy.# Mutual Information - ocenia, jakie cechy mają największą wzajemną informację z cechą docelową.# ANOVA F-test - test statystyczny do oceny różnic wśród grup i wyodrębniania cech.# XGBoost - bardziej zaawansowane podejście oparte na gradient boosting, stosowane do oceny cech na podstawie ich znaczenia.# Decision Tree - klasyfikator drzewa decyzyjnego, który może być użyty do wybrania najbardziej znaczących cech.
5.3. Tworzenie klasyfikatora
Code
model_pipeline_rf = Pipeline([('classifier', RandomForestClassifier())]) # Tworzenie potoku (pipeline) z klasyfikatorem Random Forestmodel_pipeline_lr = Pipeline([('classifier', LogisticRegression())]) # Tworzenie potoku (pipeline) z klasyfikatorem Logistic Regressionmodel_pipeline_svc = Pipeline([('classifier', SVC())]) # Tworzenie potoku (pipeline) z klasyfikatorem SVCmodel_pipeline_knn = Pipeline([('classifier', KNeighborsClassifier())]) # Tworzenie potoku (pipeline) z klasyfikatorem KNNmodel_pipeline_gb = Pipeline([ ('classifier', GradientBoostingClassifier())]) # Tworzenie potoku (pipeline) z klasyfikatorem Gradient Boosting# Tworzenie DataFrame opisującego proces tworzenia klasyfikatoraclassifier_creation_process = pd.DataFrame({'Model': ['Random Forest', 'Logistic Regression', 'SVC', 'KNN', 'Gradient Boosting'],'Pipeline Steps': ['Pipeline([("classifier", RandomForestClassifier())])','Pipeline([("classifier", LogisticRegression())])','Pipeline([("classifier", SVC())])','Pipeline([("classifier", KNeighborsClassifier())])','Pipeline([("classifier", GradientBoostingClassifier())])' ],'Description': ['Tworzenie potoku z klasyfikatorem Random Forest','Tworzenie potoku z klasyfikatorem Logistic Regression','Tworzenie potoku z klasyfikatorem SVC','Tworzenie potoku z klasyfikatorem KNN','Tworzenie potoku z klasyfikatorem Gradient Boosting' ]})# Wyświetlenie DataFramedisplay(classifier_creation_process)
Parametry: - StratifiedKFold – technika walidacji krzyżowej, która zachowuje proporcje klas w każdym foldzie (przydatne w problemach z niezbalansowanymi danymi). - n_splits=10 – podział zbioru na 10 równych części (foldów), gdzie 9 części jest używanych do trenowania, a 1 do testowania w każdej - shuffle=True – losowe przetasowanie danych przed podziałem, aby uniknąć zależności w kolejności obserwacji. - andom_state=42 – ustawienie losowego ziarna w celu zapewnienia powtarzalności wyników.
To ustawienie sprawia, że model będzie testowany na różnych częściach danych, ale proporcje klas w każdym foldzie pozostaną zachowane.
6. Ewaluacja modelu wg CV dla różnych modeli klasyfikacyjnych na ustawieniach domyślnych
celem jest:
Ocena modeli klasyfikacyjnych: Użycie walidacji krzyżowej do uzyskania wiarygodnych metryk wydajności (dokładności i F1-score) dla różnych modeli.
Wizualizacja wyników: Przedstawienie graficzne dokładności modelu w poszczególnych iteracjach walidacji krzyżowej, wraz z informacjami o średniej wydajności i odchyleniu standardowym.
Porównanie modeli: Umożliwienie wizualnego porównania wydajności różnych modeli klasyfikacyjnych na tych samych danych treningowych i przy użyciu tej samej strategii walidacji krzyżowej.
Analiza stabilności modelu: Zobaczenie, jak bardzo zmienia się dokładność modelu w różnych foldach walidacji krzyżowej, co daje wgląd w stabilność jego działania.
Code
# # ====================================================================# # Ewaluacja modelu wg CV dla różnych modeli klasyfikacyjnych na ustawieniach domyślnych# # ====================================================================import pandas as pdimport matplotlib.pyplot as pltfrom sklearn.model_selection import cross_validateimport matplotlib.pyplot as pltimport pandas as pdfrom sklearn.model_selection import cross_validatedef evaluate_model_cv(model, X_train, y_train, cv):# Cross-validation cv_results = cross_validate(model, X_train, y_train, cv=cv, scoring={'accuracy': 'accuracy', 'f1': 'f1'}) score_train = pd.DataFrame(cv_results) mean_accuracy = score_train["test_accuracy"].mean() std_accuracy = score_train["test_accuracy"].std() mean_f1 = score_train["test_f1"].mean() plt.figure(figsize=(10, 3)) score_train["test_accuracy"].plot(kind='line', marker='o', linestyle='-', color='dodgerblue', linewidth=2, markersize=6, title='Skuteczność modelu danych testowych', xlabel='', ylabel='Test Accuracy') plt.axhline(y=mean_accuracy, color='firebrick', linestyle='--', linewidth=1.1) plt.grid(axis='y', linestyle='--', linewidth=0.8, alpha=0.7) plt.gca().spines['top'].set_visible(False) plt.gca().spines['right'].set_visible(False) plt.legend(title=f'Acc: {mean_accuracy:.2f}, Std: {std_accuracy:.3f}, F1: {mean_f1:.3f}', loc='upper left')for i, v inenumerate(score_train["test_accuracy"]): plt.text(i, v +0.005, f"{v:.2f}", ha='center', fontsize=10, color='darkblue') plt.xticks(ticks=range(len(score_train)), labels=[f'Fold {i+1}'for i inrange(len(score_train))]) plt.title('Skuteczność modelu w ramach walidacji krzyżowej', fontsize=10, color='darkslategray') plt.ylabel('Test Accuracy', fontsize=10, color='dimgray') plt.show()returnprint("Model: Random Forest")evaluate_model_cv(model_rf, X_train, y_train, cv_def)print("Model: K-Nearest Neighbors")evaluate_model_cv(model_knn, X_train, y_train, cv_def)print("Model: Decision Tree")evaluate_model_cv(model_dt, X_train, y_train, cv_def)print("Model: Support Vector Classifier")evaluate_model_cv(model_svc, X_train, y_train, cv_def)print("Model: Logistic Regression")evaluate_model_cv(model_lr, X_train, y_train, cv_def)print("Model: Gradient Boosting")evaluate_model_cv(model_gb, X_train, y_train, cv_def)
Model: Random Forest
Model: K-Nearest Neighbors
Model: Decision Tree
Model: Support Vector Classifier
Model: Logistic Regression
Model: Gradient Boosting
6.2.Ocena wydajności modelu uczenia maszynowego w zależności od wartości hiperparametru.
scoring_gs = {"AUC": "roc_auc","Accuracy": make_scorer(accuracy_score),"Precision": make_scorer(precision_score),"Recall": make_scorer(recall_score),"F1-score": make_scorer(f1_score),"AUC-PR": make_scorer(average_precision_score)}import pandas as pd# Definicja tabeli z opisem metrykmetrics_info = {"Nazwa": ["AUC", "Accuracy", "Precision", "Recall", "F1-score", "AUC-PR"],"Pełna nazwa": ["Area Under the ROC Curve","Accuracy","Precision","Recall (Sensitivity)","F1-score","Average Precision Score (AUC-PR)" ],"Opis": ["Pole pod krzywą ROC, mierzy zdolność modelu do rozróżniania klas.","Odsetek poprawnie sklasyfikowanych obserwacji.","Odsetek prawidłowo przewidzianych pozytywnych przypadków wśród wszystkich przewidzianych jako pozytywne.","Odsetek prawidłowo przewidzianych pozytywnych przypadków wśród wszystkich rzeczywiście pozytywnych.","Średnia harmoniczna Precision i Recall. Dobry kompromis między nimi.","Pole pod krzywą Precision-Recall, szczególnie użyteczne dla niezbalansowanych klas." ]}df_metrics = pd.DataFrame(metrics_info)display(df_metrics)
Nazwa
Pełna nazwa
Opis
0
AUC
Area Under the ROC Curve
Pole pod krzywą ROC, mierzy zdolność modelu do rozróżniania klas.
1
Accuracy
Accuracy
Odsetek poprawnie sklasyfikowanych obserwacji.
2
Precision
Precision
Odsetek prawidłowo przewidzianych pozytywnych przypadków wśród wszystkich przewidzianych jako pozytywne.
3
Recall
Recall (Sensitivity)
Odsetek prawidłowo przewidzianych pozytywnych przypadków wśród wszystkich rzeczywiście pozytywnych.
4
F1-score
F1-score
Średnia harmoniczna Precision i Recall. Dobry kompromis między nimi.
5
AUC-PR
Average Precision Score (AUC-PR)
Pole pod krzywą Precision-Recall, szczególnie użyteczne dla niezbalansowanych klas.
7.3.Przeszukiwanie hiperparametrów
Code
print("Tabela parametrów RandomizedSearchCV: ")import pandas as pd# Dane do tabelirandom_search_info = {"Parametr": ["estimator","param_distributions","cv","scoring","n_jobs","return_train_score","refit","n_iter","random_state" ],"Wartość": ["model_rf","param_grid","cv_def","scoring_gs","-1","True","'Accuracy'","100","42" ],"Opis": ["Model poddawany optymalizacji (Random Forest w pipeline).","Rozkład wartości hiperparametrów do losowania.","Strategia walidacji krzyżowej.","Słownik metryk oceny modelu.","Liczba równoległych procesów (-1 oznacza wszystkie dostępne rdzenie CPU).","Czy zapisywać wyniki na zbiorze treningowym.","Metryka, według której zostanie wybrany najlepszy model.","Liczba losowych kombinacji hiperparametrów do przetestowania.","Seed generatora liczb losowych dla powtarzalności wyników." ]}# Tworzenie DataFramedf_random_search = pd.DataFrame(random_search_info)# Wyświetlenie tabelipd.set_option('display.max_colwidth', None)display(df_random_search)
Tabela parametrów RandomizedSearchCV:
Parametr
Wartość
Opis
0
estimator
model_rf
Model poddawany optymalizacji (Random Forest w pipeline).
1
param_distributions
param_grid
Rozkład wartości hiperparametrów do losowania.
2
cv
cv_def
Strategia walidacji krzyżowej.
3
scoring
scoring_gs
Słownik metryk oceny modelu.
4
n_jobs
-1
Liczba równoległych procesów (-1 oznacza wszystkie dostępne rdzenie CPU).
5
return_train_score
True
Czy zapisywać wyniki na zbiorze treningowym.
6
refit
'Accuracy'
Metryka, według której zostanie wybrany najlepszy model.
7
n_iter
100
Liczba losowych kombinacji hiperparametrów do przetestowania.
8
random_state
42
Seed generatora liczb losowych dla powtarzalności wyników.
7.4. Proces uczenia modelu na danych treningowych (X_train, y_train)
random_search.fit(X_train, y_train)
Wywołanie metody fit obiektu RandomizedSearchCV (lub podobnego, np. GridSearchCV), gdzie: - random_search: To instancja klasy, która implementuje losowe przeszukiwanie hiperparametrów (Randomized Search). - X_train: To zbiór danych treningowych (cechy), na którym model będzie trenowany i walidowany w procesie poszukiwania najlepszych hiperparametrów. - y_train: To etykiety odpowiadające danym treningowym, używane do oceny wydajności modelu dla różnych kombinacji hiperparametrów.
Code
import pandas as pd# Przygotowanie danych do tabelidata = {"Etap": ["Losowanie kombinacji hiperparametrów","Walidacja krzyżowa","Ewaluacja metryk","Zapis wyników","Wybór najlepszego modelu","Dostęp do najlepszych wyników" ],"Opis": ["Losuje 100 różnych zestawów hiperparametrów z podanego rozkładu param_grid.","Dla każdej kombinacji wykonuje walidację krzyżową zgodnie z obiektem cv_def.","Dla każdego podziału danych obliczane są metryki ze słownika scoring_gs: Accuracy, AUC, Precision, Recall, F1-score, AUC-PR.","Wyniki walidacji zapisywane są w atrybucie .cv_results_. Dzięki return_train_score=True zapisywane są również wyniki na zbiorze treningowym.","Na podstawie metryki Accuracy (parametr refit='Accuracy') wybierany jest najlepszy zestaw hiperparametrów. Model z tymi parametrami jest trenowany na pełnym zbiorze treningowym.","Po zakończeniu procesu dostępne są: best_params_ (najlepsze hiperparametry), best_score_ (najlepszy wynik Accuracy), best_estimator_ (najlepszy model)." ]}# Utworzenie DataFramedf_explanation = pd.DataFrame(data)# Wyświetlenie tabelipd.set_option('display.max_colwidth', None)display(df_explanation)
Etap
Opis
0
Losowanie kombinacji hiperparametrów
Losuje 100 różnych zestawów hiperparametrów z podanego rozkładu param_grid.
1
Walidacja krzyżowa
Dla każdej kombinacji wykonuje walidację krzyżową zgodnie z obiektem cv_def.
2
Ewaluacja metryk
Dla każdego podziału danych obliczane są metryki ze słownika scoring_gs: Accuracy, AUC, Precision, Recall, F1-score, AUC-PR.
3
Zapis wyników
Wyniki walidacji zapisywane są w atrybucie .cv_results_. Dzięki return_train_score=True zapisywane są również wyniki na zbiorze treningowym.
4
Wybór najlepszego modelu
Na podstawie metryki Accuracy (parametr refit='Accuracy') wybierany jest najlepszy zestaw hiperparametrów. Model z tymi parametrami jest trenowany na pełnym zbiorze treningowym.
5
Dostęp do najlepszych wyników
Po zakończeniu procesu dostępne są: best_params_ (najlepsze hiperparametry), best_score_ (najlepszy wynik Accuracy), best_estimator_ (najlepszy model).
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
8. Wybranie optymalnych hiperparametrów dla modelu
Code
# Wyświetlenie najlepszego modelu i jego parametrówbest_model = random_search.best_estimator_best_params = random_search.best_params_best_score = random_search.best_score_cv_results = random_search.cv_results_results_df = pd.DataFrame(cv_results)# Wybierz tylko kolumny z średnimi testowymi miaramimean_test_measures = [col for col in results_df.columns if col.startswith('mean_test_')]# Dodaj kolumnę params, aby mieć informacje o parametrachselected_cols = ['params'] + mean_test_measures# Wyświetl DataFrame z wybranymi kolumnami, posortowany według rankingu (jeśli istnieje)print("\nŚrednie testowe miary:")try: display(results_df[selected_cols].sort_values(by= [col for col in results_df.columns if col.startswith('mean_test__')][0]))exceptIndexError: display(results_df[selected_cols].head(1).T)
ROC AUC: Obszar pod krzywą ROC, miara zdolności modelu do rozróżniania klas.
12
Cohen's Kappa
0.701337
Cohen's Kappa: Miara zgodności między przewidywaniami a rzeczywistością, uwzględniająca losowość.
13
Matthews Correlation Coefficient
0.701476
Matthews Correlation Coefficient: Miara korelacji między przewidywaniami a rzeczywistością dla klas niezbalansowanych.
Raport klasyfikacji:
precision
recall
f1-score
support
0
0.862745
0.846154
0.854369
104.000000
1
0.838384
0.855670
0.846939
97.000000
accuracy
0.850746
0.850746
0.850746
0.850746
macro avg
0.850564
0.850912
0.850654
201.000000
weighted avg
0.850989
0.850746
0.850783
201.000000
Interpretacja Wyników Modelu
Metryki i Ich Interpretacja
TP (True Positives): 83
Model poprawnie przewidział 83 przypadki pozytywne.
FP (False Positives): 16
Model błędnie zaklasyfikował 16 przypadków jako pozytywne (błędy typu I).
FN (False Negatives): 14
Model błędnie zaklasyfikował 14 przypadków jako negatywne (błędy typu II).
TN (True Negatives): 88
Model poprawnie przewidział 88 przypadków negatywnych.
Accuracy (Dokładność): 0.850746
Model poprawnie przewidział 85.07% wszystkich przypadków.
Error Ratio (Współczynnik Błędu): 0.149254
Model błędnie przewidział 14.93% wszystkich przypadków.
Precision (Positive) (Precyzja dla Klasy Pozytywnej): 0.838384
Gdy model przewidział klasę pozytywną, miał rację w 83.84% przypadków.
Precision (Negative) (Precyzja dla Klasy Negatywnej): 0.862745
Gdy model przewidział klasę negatywną, miał rację w 86.27% przypadków.
Recall (Positive) (Pokrycie dla Klasy Pozytywnej): 0.855670
Model poprawnie zidentyfikował 85.57% wszystkich rzeczywistych przypadków pozytywnych.
Recall (Negative) (Pokrycie dla Klasy Negatywnej): 0.846154
Model poprawnie zidentyfikował 84.62% wszystkich rzeczywistych przypadków negatywnych.
F1-score: 0.846939
Średnia harmoniczna precyzji i pokrycia, wskazująca na zrównoważoną wydajność.
ROC AUC: 0.934774
Obszar pod krzywą ROC, wskazujący na dobrą zdolność modelu do rozróżniania klas.
Cohen’s Kappa: 0.701337
Miara zgodności między przewidywaniami a rzeczywistością, uwzględniająca losowość.
Matthews Correlation Coefficient (MCC): 0.701476
Miara korelacji między przewidywaniami a rzeczywistością, szczególnie przydatna dla niezbalansowanych klas.
Precision (Positive) (Precyzja dla Klasy Pozytywnej): 0.838384
Gdy model przewidział klasę pozytywną, miał rację w 83.84% przypadków.
Precision (Negative) (Precyzja dla Klasy Negatywnej): 0.862745
Gdy model przewidział klasę negatywną, miał rację w 86.27% przypadków.
Recall (Positive) (Pokrycie dla Klasy Pozytywnej): 0.855670
Model poprawnie zidentyfikował 85.57% wszystkich rzeczywistych przypadków pozytywnych.
Recall (Negative) (Pokrycie dla Klasy Negatywnej): 0.846154
Model poprawnie zidentyfikował 84.62% wszystkich rzeczywistych przypadków negatywnych.
Podsumowanie
Model wykazuje ogólnie dobrą wydajność, z dokładnością wynoszącą około 85%.
Precyzja i pokrycie są dość zrównoważone dla obu klas, co jest pozytywne.
Wysoka wartość ROC AUC (0.93) wskazuje na dobrą zdolność modelu do rozróżniania klas.
Wartości Cohen’s Kappa i MCC wskazują na umiarkowaną do dobrej zgodność przewidywań z rzeczywistością.
Ogólnie rzecz biorąc, model jest dobrze dopasowany do danych.
Raport klasyfikacji dostarcza miary (precision, recall, f1-score) dla każdej z klas oddzielnie. Oznacza to, że: - Miary dla klasy 0 pokazują, jak dobrze model radzi sobie z przewidywaniem przypadków należących do klasy 0. - Miary dla klasy 1 pokazują, jak dobrze model radzi sobie z przewidywaniem przypadków należących do klasy 1.
Pokazuje rozkład przewidywanych prawdopodobieństw dla klas.
Ocenia, jak model rozdziela klasy.
Wykres F1-score vs. Próg:
Pokazuje, jak zmienia się F1-score w zależności od progu decyzyjnego.
F1-score jest średnią harmoniczną precyzji i pokrycia, co pozwala na ocenę zrównoważonej wydajności modelu.
Pomaga w wyborze optymalnego progu, który maksymalizuje F1-score.
Wykres FPR vs. FNR: * Pokazuje, jak zmieniają się False Positive Rate (FPR) i False Negative Rate (FNR) w zależności od progu decyzyjnego. * FPR mierzy odsetek błędnie zaklasyfikowanych negatywnych przypadków jako pozytywne. * FNR mierzy odsetek błędnie zaklasyfikowanych pozytywnych przypadków jako negatywne. * Pomaga w wyborze progu, który minimalizuje błędy obu typów, w zależności od potrzeb aplikacji.
Skumulowana Dystrybucja Prawdopodobieństw: * Pokazuje skumulowaną dystrybucję przewidywanych prawdopodobieństw dla klas pozytywnej i negatywnej. * Pozwala ocenić, jak dobrze model rozdziela klasy i jak rozkładają się prawdopodobieństwa. * Ułatwia zrozumienie, jak zmienia się odsetek przypadków w zależności od progu.
Wykres Punktowy Prawdopodobieństw: * Pokazuje rozkład przewidywanych prawdopodobieństw dla każdej próbki w zbiorze testowym. * Kolor punktów reprezentuje rzeczywistą klasę (pozytywną lub negatywną). * Pozwala na wizualną ocenę, jak dobrze model rozdziela klasy i jakie są przewidywane prawdopodobieństwa dla poszczególnych próbek.
Wykresy te umożliwię wszechstronną ocenę modelu klasyfikacji binarnej, uwzględniając różne aspekty jego wydajności. Wykresy i metryki pozwalają na identyfikację mocnych i słabych stron modelu oraz na dostosowanie jego parametrów w celu optymalizacji wyników.